文章目录
  1. 1. Retrofit的使用
  2. 2. Retrofit源码探秘
    1. 2.1. Retrofit注解定义和解析
    2. 2.2. Retrofit的Call与Response
    3. 2.3. Retrofit的Converter与CallAdapter: 开始使用RxJava
  3. 3. 参考

截至我写文章为止, Retrofit更新到了2.2.0版本. 据说1.x2.0+差异巨大, 这里我们就直接拥抱变化来看最新代码了.

Retrofit的使用

添加依赖和添加网络权限我就不说了. 假设我们要访问网络获取一个Json格式的响应, 来看代码怎么写:

Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://url.of.yourserver/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build(); // 创建全局管理
HttpService httpService = retrofit.create(YourHttpService.class); // 创建访问接口实例
Call<Json> call = httpService.getJson(); // 为要访问的接口创建Call
call.enqueue(new YourCallbackForThisCall(){...}); // Call入队列并添加回调

非常简单对不对, 而且YourHttpService.class甚至可能根据某种规范直接从服务端导出, 需要自己手写的代码几乎可以忽略不计, 出bug的几率就更小了.
这里的Callback经我验证是在UI线程执行的, 所以可以直接在onResponse里更新UI. 如果想要换到其他线程处理, 需要设置callbackExecutor, 默认是主线程.

Retrofit源码探秘

根据我们前面分析过的OkHttp源码可以知道, Retrofit背后的请求发送和接收都是通过它来完成的, 自己几乎没做什么. Retrofit能实现这么简单的调用方式, 其魔法就在它的抽象架构上. Retrofit通过定义和解析注解的方式生成Request, 又在原本OkHttpCall(Retrofit定义的OkHttp的代理)的回调之中加了一层, 用于处理响应出错情况和把响应结果包装成ExceptionCatchingRequestBody, 最后用ServiceMethod.toResponse使用Converter完成响应解析, 使用解析结果通过CallAdapter构造一个自定义的Response<T>并返回. 再在外层回调我们设置的Callback<T>.
真正的网络请求的事情包括连接和缓存的维护都交给OkHttpClient完成了, 因此关键就在于Retrofit从注解到Call的过程和从ResponseResponse<T>的过程.

Retrofit注解定义和解析

在我之前的某篇文章中曾经解析过JavaAnnotation机制, 这里就不再解释了, 我们已经知道, Java的注解是通过注解处理器Processor进行处理的, Retrofit应该也是实现了类似的机制去处理它自己的注解. 注解都是编译器完成处理的, 其处理后的代码的生命周期根据它的Retention不同而不同, RetentionRUNTIME的注解在编译器处理之后存在于.class文件中并且还被虚拟机载入, 所以可供后续处理. 接下来我们就来看看Retrofit中的注解定义和解析器的定义, 我们将会发现它所有的注解都是RUNTIME的.
注解都在http包下, 从名字也看得出来基本上对应了http协议中的各个实体. 我们只看其中GET Headers Streaming三个.
选这三个的原因是三个在http协议中分别代表三种类型: 请求方法, 结构参数, 传输标记. 如果不记得http协议的同学可以看我另一篇博客Android源码解析-网络架构.
三个注解的完整代码如下:

@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
  /**
   * A relative or absolute path, or full URL of the endpoint. This value is optional if the first
   * parameter of the method is annotated with {@link Url @Url}.
   * <p>
   * See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
   * this is resolved against a base URL to create the full endpoint URL.
   */
  String value() default "";
}

/**
 * Adds headers literally supplied in the {@code value}.
 * <pre><code>
 * &#64;Headers("Cache-Control: max-age=640000")
 * &#64;GET("/")
 * ...
 *
 * &#64;Headers({
 *   "X-Foo: Bar",
 *   "X-Ping: Pong"
 * })
 * &#64;GET("/")
 * ...
 * </code></pre>
 * <strong>Note:</strong> Headers do not overwrite each other. All headers with the same name will
 * be included in the request.
 *
 * @see Header
 * @see HeaderMap
 */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Headers {
  String[] value();
}

/**
 * Treat the response body on methods returning {@link okhttp3.Response Response} as is,
 * i.e. without converting {@link okhttp3.Response#body() body()} to {@code byte[]}.
 */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Streaming {
}

为避免各位说我凑篇幅, 请各位仔细看上文代码的注释部分和注解部分, 我们可以看到这几个注解的设计思路, 基本上是按照http协议的基本部分来分解的, 并且都有明确的作用对象和作用范围.
对这三种类型的注解的处理自然也是不太一样, 接下来我们就看看Retrofit为它的这些注解编写了怎样的处理器. 注解的转存在ServiceMethod.Builder()中, 处理则在ServiceMethod.Builder.build()中, 如下:

public ServiceMethod build() {
  ...
  // Method级别的Annotation, 主要是GET/POST等方法
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
  ...
  // Parameter级别的Annotation, 主要是Path等
  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
      throw parameterError(p, "No Retrofit annotation found.");
    }

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }
 ...
}

// 处理Method级别Annotation
private void parseMethodAnnotation(Annotation annotation) {
  if (annotation instanceof DELETE) {
    parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
  } else if (annotation instanceof GET) {
    parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
  } else if (annotation instanceof HEAD) {
    parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
    if (!Void.class.equals(responseType)) {
      throw methodError("HEAD method must use Void as response type.");
    }
  } else if (annotation instanceof PATCH) {
    parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
  } else if (annotation instanceof POST) {
    parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
  } else if (annotation instanceof PUT) {
    parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  } else if (annotation instanceof OPTIONS) {
    parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
  } else if (annotation instanceof HTTP) {
    HTTP http = (HTTP) annotation;
    parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
  } else if (annotation instanceof retrofit2.http.Headers) {
    String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
    if (headersToParse.length == 0) {
      throw methodError("@Headers annotation is empty.");
    }
    headers = parseHeaders(headersToParse);
  } else if (annotation instanceof Multipart) {
    if (isFormEncoded) {
      throw methodError("Only one encoding annotation is allowed.");
    }
    isMultipart = true;
  } else if (annotation instanceof FormUrlEncoded) {
    if (isMultipart) {
      throw methodError("Only one encoding annotation is allowed.");
    }
    isFormEncoded = true;
  }
}

// 处理Parameter级别Annotation
private ParameterHandler<?> parseParameter(
    int p, Type parameterType, Annotation[] annotations) {
  ParameterHandler<?> result = null;
  for (Annotation annotation : annotations) {
    ParameterHandler<?> annotationAction = parseParameterAnnotation(
        p, parameterType, annotations, annotation);

    ...// handleException

    result = annotationAction;
  }
  ...// handleException

  return result;
}

// 具体的处理Annotation的实现, 方法颇长就不贴详情了
private ParameterHandler<?> parseParameterAnnotation(
    int p, Type type, Annotation[] annotations, Annotation annotation) {
  if (annotation instanceof Url) { // handle @Url
    ...// throw Exception if necessary

    gotUrl = true;

    if (type == HttpUrl.class
        || type == String.class
        || type == URI.class
        || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
      return new ParameterHandler.RelativeUrl();
    } else {
      ...// throw Exception
    }

  } else if (annotation instanceof Path) { // handle @Path
    ...// throw Exception if necessary

    gotPath = true;

    Path path = (Path) annotation;
    String name = path.value();
    validatePathName(p, name);

    Converter<?, String> converter = retrofit.stringConverter(type, annotations); // 见Retrofit.stringConverter()
    return new ParameterHandler.Path<>(name, converter, path.encoded()); // 见ParameterHandler.Path<>

  } else if (annotation instanceof Query) { // handle @Query
  } else if (annotation instanceof QueryName) { // handle @QueryName
  } else if (annotation instanceof Header) { // handle @Header
  } else if (annotation instanceof FieldMap) { // handle @FieldMap
  } else if (annotation instanceof Part) { // handle @Part
  } else if (annotation instanceof PartMap) { // handle @PartMap
  } else if (annotation instanceof Body) { // handle @Body
  }
  return null; // Not a Retrofit annotation.
}

可见Retrofit对注解的处理分为两个部分: 一个是Method级别的Annotation, 所有http协议方法都被定义为该级别的Annotation, 在ServiceMethod.parseHttpMethodAndPath中处理(注意到, 基本方法DELETE GET HEAD POST等应该是互斥的, 但代码中没有进行判断, 而MultipardFormUrlEncoded之间却进行了判断, 至于代码中没有对Streaming的处理, 是因为逻辑上它不在这一层, 而是在响应结果的解析, 所以其实是在retrofit2/BuiltInConverters.java中进行了处理); 另一个是Parameter级别的, 大多数是在进行基本预处理之后就委托给ParameterHandler接口的实现类进行处理.

通过对某个方法的注解的处理, 在ServiceMethod.Builder.build()完成后ServiceMethod就获取到了调用该方法发起网络请求所需要的信息, 可以开始构造Call了.

Retrofit的Call与Response

RetrofitCallResponse是对OkHttpCallResponse的封装, 为什么要进行这么一层封装呢? 接下来我们就看看它的源码.
我们选择从核心方法Retrofit.create(Class)开始.

public <T> T create(final Class<T> service) {
  Utils.validateServiceInterface(service); // #1
  if (validateEagerly) {
    eagerlyValidateMethods(service); // #2
  }

  // #3 below
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
        private final Platform platform = Platform.get();

        @Override public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          ServiceMethod<Object, Object> serviceMethod =
              (ServiceMethod<Object, Object>) loadServiceMethod(method); // #4
          OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); // #5
          return serviceMethod.callAdapter.adapt(okHttpCall); // #6
        }
      });
}

这里面一共有三个步骤, 其中前两步是用来检查接口和提前载入方法的, 第三步是关键, 生成并返回了一个代理. 实例化代理时传入了三个参数, 第三个是InvocationHandler的实现, 我们可以看到里面最关键的步骤是#4 #5 #6, 分别完成方法载入(Method->ServiceMethod), Call构造和返回Call代理. 这里ProxyInvocationHandler都是Java中就已经存在并引入了Android中的机制, 即动态代理机制. 关于动态代理机制, 思想成熟战法犀利, 是相当有意思的一个东西, 可以参见这篇文章
总之要使用动态代理呢, 委托类必须要实现某个接口, 而Proxy创建的代理类通过实现InvocationHandler来调用委托类的方法, 完成代理. 所有这一切都跟反射密不可分, 所以理所当然Proxy是在反射包下的.
从上面代码可以看出, Platform.isDefaultMethod()Object的方法将被InvocationHandler直接代理执行, 而其他方法则被包装成OkHttpCall交给ServiceMethodCallAdapter去执行. 这里也可以看出对CallResponse进行封装代理的作用: 屏蔽底层实现, 统一处理非核心逻辑, 统一接口, 令底层替换成其他网络访问库成为可能.

下面我们来看ServiceMethod的构造过程, OkHttpCall的构造过程以及默认的CallAdapter的代码.
还记得在create里传入loadServiceMethod的是Method method, 它是代理方法, 通过它去访问委托(被代理)方法, 代码如下:

ServiceMethod<?, ?> loadServiceMethod(Method method) {
  ServiceMethod<?, ?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = new ServiceMethod.Builder<>(this, method).build(); // 传入Retrofit, Method实例, 构造代理包装类ServiceMethod实例
      serviceMethodCache.put(method, result); // 缓存代理方法-代理包装实例映射
    }
  }
  return result;
}

然后根据传入的方法及其参数, 构造一个合适的Call, 这个CallOkHttpCall的代理, 所以Retrofit中它的类是OkHttpCall. 下面我们看它是怎么构造和代理的, 代码如下:

final class OkHttpCall<T> implements Call<T> {
    OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
      this.serviceMethod = serviceMethod;
      this.args = args;
    }

    @Override public void enqueue(final Callback<T> callback) {
      // 检查参数

      okhttp3.Call call;
      Throwable failure;

      synchronized (this) {
        if (executed) throw new IllegalStateException("Already executed.");
        executed = true;

        call = rawCall;
        failure = creationFailure;
        if (call == null && failure == null) {
          try {
            call = rawCall = createRawCall();
          } catch (Throwable t) {
            failure = creationFailure = t;
          }
        }
      }

      if (failure != null) {
        callback.onFailure(this, failure);
        return;
      }

      if (canceled) {
        call.cancel();
      }

      call.enqueue(new okhttp3.Callback() { // okhttp3.Callback反向代理retrofit2.Callback
        @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
            throws IOException {
          Response<T> response;
          try {
            response = parseResponse(rawResponse); // 解析响应
          } catch (Throwable e) {
            callFailure(e); // 回调自身callFailure
            return;
          }
          callSuccess(response); // 回调自身callSuccess
        }

        @Override public void onFailure(okhttp3.Call call, IOException e) {
          try {
            callback.onFailure(OkHttpCall.this, e);
          } catch (Throwable t) {
            t.printStackTrace();
          }
        }

        private void callFailure(Throwable e) {
          try {
            callback.onFailure(OkHttpCall.this, e);
          } catch (Throwable t) {
            t.printStackTrace();
          }
        }

        private void callSuccess(Response<T> response) {
          try {
            callback.onResponse(OkHttpCall.this, response);
          } catch (Throwable t) {
            t.printStackTrace();
          }
        }
      });
    }
}

结构清晰, 基本可以当做OkHttpCall来对待.

接下来通过ServiceMethod.callAdapter.adapt(OkHttpCall)OkHttpCall转换为Call<T>. ServiceMethod的建造模式如下:

Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations(); # 获取代理方法的方法注解
  this.parameterTypes = method.getGenericParameterTypes(); # 获取泛型参数类型
  this.parameterAnnotationsArray = method.getParameterAnnotations(); # 获取参数注解
}

public ServiceMethod build() {
  callAdapter = createCallAdapter(); # 根据泛型返回类型和方法注解, 通过retrofit.callAdapter(returnType, annotations)寻找适配器实例. Retrofit实例化时默认添加`DefaultCallAdapterFactory`工厂
  responseType = callAdapter.responseType(); # 获取返回类型
  if (responseType == Response.class || responseType == okhttp3.Response.class) {
    throw methodError("'"
        + Utils.getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?"); # 不允许以`Response`作为返回类型
  }
  responseConverter = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation); # 解析方法注解
  }

  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }

  ...// 判断方法注解合法性

  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    // 解析参数注解
    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    ...
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  ...// 判断参数合法性

  return new ServiceMethod<>(this);
}

ServiceMethod(Builder<R, T> builder) {
  this.callFactory = builder.retrofit.callFactory();
  this.callAdapter = builder.callAdapter;
  this.baseUrl = builder.retrofit.baseUrl();
  this.responseConverter = builder.responseConverter;
  this.httpMethod = builder.httpMethod;
  this.relativeUrl = builder.relativeUrl;
  this.headers = builder.headers;
  this.contentType = builder.contentType;
  this.hasBody = builder.hasBody;
  this.isFormEncoded = builder.isFormEncoded;
  this.isMultipart = builder.isMultipart;
  this.parameterHandlers = builder.parameterHandlers;
}

始终别忘了以上内容都是通过Java自带的Proxy机制调用InvocationHandler来代理的, 所以create传入的是什么类, 返回的就是什么类的代理, 访问这个代理的方法时, 实际调用的是InvocationHandler. 通过ServiceMethod.callAdapter.adapt()可以将传入的方法适配为你自己定义的返回类型Call<T>的实例. 我们来看看默认情况下添加的DefaultCallAdapterFactory会生成什么CallAdapter:

public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { // 工厂方法, 传入返回类型, 注解数组, retrofit实例
  if (getRawType(returnType) != Call.class) { // 返回类型如果不是Call则直接返回null
    return null;
  }

  final Type responseType = Utils.getCallResponseType(returnType); // 获取ReturnType<T>中泛型参数T的上限类型
  return new CallAdapter<Object, Call<?>>() {
    @Override public Type responseType() {
      return responseType;
    }

    @Override public Call<Object> adapt(Call<Object> call) {
      return call; // 默认返回传入参数
    }
  };
}

显然默认情况下这个适配器什么也不做, 直接把传入的OkHttpCall返回给你.

Retrofit的Converter与CallAdapter: 开始使用RxJava

上文已经暗示了, 对结果的解析器Converter可以替换, 其实发送请求的代理Call也可以被替换, 只要换掉CallAdapter就可以了, 网上都说RxJava+Retrofit可以打出成吨输出, 原因就在于CallAdapter可以换成一个返回Observable类型的适配器, 这使得在Retrofit.create()之后可以直接以RxJava的风格做链式调用.
实现起来也非常简单, 只要.addConverterFactory() .addCallAdapterFactory()即可, 因为Retrofit对外提供了抽象类Converter.Factory CallAdapter.Factory, 并且实例全部使用泛型来表示, 通过对外提供统一接口, 只要外部可以继承抽象工厂类并返回实现了统一接口的实例类, 就可以在不改变Retrofit一行代码的情况下接入其他库, 或者原有逻辑代码变动不大的情况下接入Retrofit.

是时候上一波图了:
Retrofit, RxJava 和 OkHttp之间的关系
上面这张图是我基于Stay的这篇文章改的, 荣耀归他, 另外由于图滞后于Retrofit版本, 所以如果有不一致的地方锅也归他(目前没看见不一致).

参考

这篇文章分析了Retrofit的实现, 其优缺点即使不是一目了然, 也是足够清晰了, 希望能够帮助到正在选择网络请求框架的各位.
本文参考如下:

文章目录
  1. 1. Retrofit的使用
  2. 2. Retrofit源码探秘
    1. 2.1. Retrofit注解定义和解析
    2. 2.2. Retrofit的Call与Response
    3. 2.3. Retrofit的Converter与CallAdapter: 开始使用RxJava
  3. 3. 参考